﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Web.Mvc;
using Ionic.Zip;

namespace CloudStorageLight.Core.Web
{
    public static class WebUtil
    {
        public static long? GetUsedStoargeSize(string account)
        {
            if (HttpContext.Current == null) return null;
            var sizeDict = (Dictionary<string, long>)HttpContext.Current.Application["UsedStoargeSize"];
            if (sizeDict.ContainsKey(account))
            {
                return sizeDict[account];
            }
            return null;
        }

        public static string GetUsedStoargeSizeString(string account)
        {

            var size = GetUsedStoargeSize(account);
            if (!size.HasValue) return "";
            return SizeSuffix(size.Value);
        }

        static readonly string[] SizeSuffixes = { "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
        public static string SizeSuffix(Int64 value)
        {
            if (value < 0) { return "-" + SizeSuffix(-value); }
            if (value == 0) { return ""; }

            int mag = (int)Math.Log(value, 1024);
            decimal adjustedSize = (decimal)value / (1L << (mag * 10));

            return string.Format("{0:n1} {1}", adjustedSize, SizeSuffixes[mag]);
        }

        public static string GetIPAddress()
        {
            System.Web.HttpContext context = System.Web.HttpContext.Current;
            if (context == null) return null;
            string ipAddress = context.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];

            if (!string.IsNullOrEmpty(ipAddress))
            {
                string[] addresses = ipAddress.Split(',');
                if (addresses.Length != 0)
                {
                    return addresses[0];
                }
            }

            return context.Request.ServerVariables["REMOTE_ADDR"];
        }

        /// <summary>
        /// ZIPでダウンロード
        /// </summary>
        /// <param name="fc"></param>
        /// <returns></returns>
        public static async Task<string> CreateZipFile(BlobService svc, BlobUser user, List<string> pathList)
        {

            var tempzip = Path.GetTempFileName();
            var tempList = new List<string>();
            try
            {
                using (var zip = new ZipFile())
                {
                    zip.AlternateEncoding = Encoding.UTF8;
                    zip.AlternateEncodingUsage = ZipOption.AsNecessary;

                    foreach (var path in pathList)
                    {
                        var temp = Path.Combine(SystemSettings.Instance.WorkFolder, Guid.NewGuid().ToString() + Path.GetExtension(path));
                        tempList.Add(temp);
                        using (var source = new FileStream(temp, FileMode.Create))
                        {
                            await svc.DownloadFileAsync(user, path, source);
                            source.Flush();
                        }

                        var passcode = await svc.GetFilePasscodeAsync(path);
                        var protectedMode = await svc.GetProtectedModeAsync(path);

                        var userPasscode = GetUserEncryptedFileProtectedPassword(svc, user);
                        if (protectedMode.EndsWith("UserPassword") && !string.IsNullOrEmpty(userPasscode))
                        {
                            passcode = CryptUtil.DecryptStringAES(userPasscode, SystemSettings.Instance.GeneralEncryptKey);
                        }

                        if (protectedMode == "Password" || protectedMode == "UserPassword")
                        {
                            if (path.EndsWith(".pdf", StringComparison.InvariantCultureIgnoreCase))
                            {
                                if (!PdfCryptoUtil.IsPasswordProtected(temp))
                                {
                                    PdfCryptoUtil.SetPassword(temp, passcode);
                                }
                            }
                            else if (MsOffCryptoUtil.IsOfficeFilePasscode(path))
                            {
                                if (!MsOffCryptoUtil.IsEncryptedPackage(temp))
                                {
                                    MsOffCryptoUtil.SetPassword(temp, passcode);
                                }
                            }
                        }
                        else if (protectedMode == "RMS")
                        {
                            if (!RmsUtil.IsFileEncrypted(temp))
                            {
                                if (!MsOffCryptoUtil.IsRMSProtectedFilename(path))
                                {
                                    var temp2 = await MsOffCryptoUtil.EncryptRmsFile(svc, path, temp);
                                    if (temp != temp2) tempList.Add(temp2);
                                    temp = temp2;
                                }
                                else
                                {
                                    throw new BlobException(BlobExceptionCode.CanNotProtect, BlobItem.GetFileName(path));
                                }
                            }
                            else
                            {
                                throw new BlobException(BlobExceptionCode.CanNotProtect, BlobItem.GetFileName(path));
                            }
                        }

                        var e = zip.AddFile(temp);
                        e.FileName = Path.GetFileNameWithoutExtension(BlobItem.GetFileName(path)) + Path.GetExtension(temp);

                        var blobFile = await svc.GetFileAsync(user, path);
                        var lastModified = blobFile.LastWriteTime ?? blobFile.LastModified;
                        if (lastModified.HasValue) e.LastModified = lastModified.Value.DateTime;

                        var creationTime = blobFile.CreationTime ?? blobFile.LastModified;
                        if (creationTime.HasValue) e.CreationTime = creationTime.Value.DateTime;
                        if (IsPasswordProtectMode(protectedMode))
                        {
                            if (passcode == null) throw new InvalidOperationException("passcode is null");
                            e.Password = passcode;
                        }
                    }

                    zip.Save(tempzip);
                }

                return tempzip;

            }
            finally
            {
                foreach (var temp in tempList)
                {
                    if (System.IO.File.Exists(temp)) System.IO.File.Delete(temp);
                }
            }
        }

        private static bool IsPasswordProtectMode(string protectedMode)
        {
            if (protectedMode == "Password" || protectedMode == "UserPassword") return true;
            if (protectedMode == "ZipPassword" || protectedMode == "ZipUserPassword") return true;
            return false;
        }

        static string GetUserEncryptedFileProtectedPassword(BlobService svc, BlobUser u)
        {
            var info = svc.GetUserInfo(u);
            return info == null ? null : info.EncryptedFileProtectedPassword;
        }

        public static bool ValidUrl(string url)
        {
            Uri validatedUri;

            if (Uri.TryCreate(url, UriKind.Absolute, out validatedUri)) //.NET URI validation.
            {
                //If true: validatedUri contains a valid Uri. Check for the scheme in addition.
                return (validatedUri.Scheme == Uri.UriSchemeHttp || validatedUri.Scheme == Uri.UriSchemeHttps);
            }
            return false;
        }
    }
}
